<?php
/**
 * 该类主要完成小程序授权逻辑处理
 * Created by PhpStorm.
 * User: zd
 * Date: 2018/10/22
 * Time: 09:49
 */

namespace app\api\service;


use app\lib\exception\MiniSuiteException;
use \app\api\model\Mini as MiniModel;
use think\Loader;
use think\Request;

Loader::import('WxMini.wxBizMsgCrypt',EXTEND_PATH);
class Mini
{
    protected $token;
    protected $encodingAesKey;
    protected $appId;
    protected $appSecret;
    protected $componentAccessToken;

    function __construct()
    {
        $this->token = config('mini.token');
        $this->encodingAesKey = config('mini.key');
        $this->appId = config('mini.AppID');
        $this->appSecret = config('mini.AppSecret');
    }

    /**
     * h获取微信传递消息
     * @return mixed|string
     * @throws MiniSuiteException
     */
    public function getNotify(){
        $getData = Request::instance()->param();
        $postData = file_get_contents('php://input');
        $sMsg = "";  // 解析之后的明文
        $wxcpt = new \WXBizMsgCrypt( $this->token,$this->encodingAesKey,$this->appId);
        $from_xml = $this->handleXML($postData);//必须为post接收的xml格式数据
        $errCode =  $wxcpt->decryptMsg($getData['msg_signature'],$getData['timestamp'],$getData['nonce'],$from_xml,$sMsg);
        if ($errCode == 0) {
            $data =\Utils::Xml2Array($sMsg);
            //error_log(print_r($data, 1), 3, "./audit.txt");
            $this->getInfoType($data);
            return $data;
        } else {
            return  "ERR: " . $errCode . "\n\n";
        }
    }
    /**
     * 信息类型处理
     * @param $data
     * @throws MiniSuiteException
     */
    public function getInfoType(array $data){
        switch ($data['InfoType']){
            case 'component_verify_ticket' :
                if (!empty($data['ComponentVerifyTicket'])){
                    $data['ComponentVerifyTicket'] = preg_replace("/ticket@@@/","",$data['ComponentVerifyTicket']) ;//处理解密后的字符串
                    $this->saveToCache($data);
                }else{
                    //错误处理
                    throw new MiniSuiteException();
                }
                break;
            case 'authorized';//授权成功
                //error_log(print_r($data, 1), 3, "./audit.txt");
                break;
            case 'unauthorized';//取消授权
                //error_log(print_r($data, 1), 3, "./audit.txt");
                MiniModel::cancelMini($data['AuthorizerAppid']);//取消授权
                break;
            case 'updateauthorized';//变更授权

                break;
        }
    }
    /**
     * 第二步：获取微信第三方的component_access_token，并缓存防止接口预警
     * @return mixed
     */
    public function getComponentAccessToken(){
        $jsonData = cache($this->appId);
        $data = json_decode($jsonData);
        $componentVerifyTicket = $data->ComponentVerifyTicket;
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
        $params = [
            'component_appid'=>$this->appId,
            'component_appsecret'=>$this->appSecret,
            'component_verify_ticket'=>$componentVerifyTicket
        ];
        $requsetData = curl_post($url,$params);
        cache('component',$requsetData,config('setting.component_expire_in'));
        return $requsetData;
    }
    /**
     * 第三步：获取预授权码
     * @return mixed
     * @throws MiniSuiteException
     */
    public function getPreAuthCode(){
        $componentAccessToken = $this->getComponentValue();//获取component_access_token
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%s";
        $url = sprintf($url,$componentAccessToken);
        $params = [
            'component_appid'=>$this->appId,
        ];
        $requsetData = curl_post($url,$params);//请求数据
        $requsetData = json_decode($requsetData);
        if (property_exists($requsetData,'pre_auth_code') ){
            $requsetData->pre_auth_code = preg_replace("/preauthcode@@@/","",$requsetData->pre_auth_code);//处理解密后的字符串
            return $requsetData;
        }else{
            throw new MiniSuiteException([
                'msg'=>'获取预授权码失败'.$requsetData,
                'error_code'=>'60000'
            ]);
        }
    }

    /**
     * 返回授权连接
     * @param $token
     * @return string
     * @throws MiniSuiteException
     */
    public function getAuthUrl($token){
        $appid = $this->appId;
        $requsetData = $this->getPreAuthCode();
        $pre_auth_code = $requsetData->pre_auth_code;
        $redirect_uri =urlencode('https://business.17jxyx.com/index/v1/mini/get_uri?token='.$token);//一定要加协议头
        $url = 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid='.$appid.'&pre_auth_code='.$pre_auth_code.'&redirect_uri='.$redirect_uri.'&auth_type=2';
        return $url;
    }
    /**
     * 第4步：接收预授权码，获取小程序详细信息
     * @param $info
     * @return $this
     * @throws MiniSuiteException
     */
    public function getUriData($uid,$info){
        $componentAccessToken = $this->getComponentValue();//获取component_access_token
        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%s';
        $url = sprintf($url,$componentAccessToken);
        $auth_code = preg_replace("/queryauthcode@@@/","",$info['auth_code']);
        $params = [
            'component_appid'=>$this->appId,
            'authorization_code'=>$auth_code,
        ];
        $requsetData = curl_post($url,$params);//请微信接口获取凭据和授权信息
        $data = json_decode($requsetData);
        if (property_exists($data,'authorization_info')){
            $authorization_info = $data->authorization_info;
            $appid = $authorization_info->authorizer_appid;//授权方的APPID
            $authorizer_access_token = $authorization_info->authorizer_access_token;//授权方的access_token
            $expires_in = $authorization_info->expires_in;//access_token的过期时间
            cache($appid,json_encode($authorizer_access_token),$expires_in);//缓存授权方的access_token
            $status =  $this->getAuthInfo($uid,$appid);
           return $status;
        }else{
            throw new MiniSuiteException([
                'msg'=>'获取接口调用凭证失败'.$data,
                'error_code'=>'61000'
            ]);
        }
    }
    /**
     * 第五步：获取小程序详细信息
     * @param $uid
     * @param $appid
     * @return $this
     * @throws MiniSuiteException
     */
    public function getAuthInfo($uid,$appid){
        $componentAccessToken = $this->getComponentValue();//获取component_access_token
        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s';
        $url = sprintf($url,$componentAccessToken);
        $params = [
            'component_appid'=>$this->appId,
            'authorizer_appid'=>$appid,
        ];
        $requsetData = curl_post($url,$params);//获取微信小程序的详细信息
        $data = json_decode($requsetData);
         if (property_exists($data,'authorizer_info')){
             $status = MiniModel::createMini($uid,$data);
             return $status;
        }else{
            throw new MiniSuiteException([
                'msg'=>'获取小程序详情失败'.$data,
                'error_code'=>'62000'
            ]);
        }
    }


    /**
     * 刷新access_tokan
     * @param $appid
     * @return bool
     * @throws MiniSuiteException
     */
    public function getAccessToken($appid){
        $miniInfo = MiniModel::getMini($appid);
        $componentAccessToken = $this->getComponentValue();//获取component_access_token
        $url = 'https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%s';
        $url = sprintf($url,$componentAccessToken);
        $params = [
            'component_appid'=>$this->appId,
            'authorizer_appid'=>$appid,
            'authorizer_refresh_token'=>$miniInfo->authorizer_refresh_token
        ];
        $requsetData = curl_post($url,$params);//请微信接口获取凭据和授权信息
        $data = json_decode($requsetData);
            $authorizer_access_token = $data->authorizer_access_token;//授权方的access_token
            $expires_in = $data->expires_in;//access_token的过期时间
            cache($appid,json_encode($authorizer_access_token),$expires_in);//缓存授权方的access_token
        if (property_exists($data,'authorizer_access_token')){
            return $authorizer_access_token;
        }else{
            throw new MiniSuiteException([
               'msg'=>'更新access_token失败',
                'error_code'=>'63000'
            ]);
        }
    }
    /**
     * 处理微信传递过来的xml数据
     * @param $getData
     * @param $encryptMsg
     * @param $sMsg
     * @return int
     */
    protected function handleXML($encryptMsg){
        $xml_tree = new \DOMDocument();
        $xml_tree->loadXML($encryptMsg);
        $array_e = $xml_tree->getElementsByTagName('Encrypt');
        $encrypt = $array_e->item(0)->nodeValue;
        $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
        $from_xml = sprintf($format, $encrypt);
        return $from_xml;
    }
    /**
     * 将接收到的suite_tikect存入缓存中，无论缓存是否失效
     * @param $caheValue
     * @return bool
     * @throws WorkSuiteTicketException
     */
    private function saveToCache($caheValue){
        $key =  $caheValue['AppId'] ;
        $value = json_encode($caheValue);
        $expire_in = config('setting.suite_expire_in');
        $result =  cache($key,$value,$expire_in);
        if (!$result){
            return true;
        }
        return $key;
    }
    /**
     * 获取componentAccessToken缓存
     * @return mixed
     */
    public function getComponentValue(){
        if (!cache('component')){
            $jsonData = $this->getComponentAccessToken();//缓存不存在则获取
        }else{
            $jsonData = cache('component');
        }
        $data = json_decode($jsonData);
        $componentAccessToken = $data->component_access_token;//获取component_access_token
        return $componentAccessToken;
    }
}